﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JESAI.Schedule.Cron.Helpers
{
    internal static class TimeZoneHelper
    {
        /// <summary>
        /// 检查给定时间在给定时区中是否不明确。
        /// </summary>
        /// <summary>
        /// 这种方法之所以存在，是因为。NET框架。NET Core和Mono在从标准时间（ST）过渡到
        /// 夏令时（DST）发生。
        /// 夏令时结束时，您可以将时钟向后设置。如果你在美国，它发生在11月的第一个星期日凌晨2:00。
        /// 因此，从凌晨1:00到凌晨2:00的持续时间重复两次。
        /// .NET框架和。NET Core将向后夏令时转换视为[1:00夏令时->2:00夏令时）--[1:00 ST-->2:00 ST]。所以2:00不是模棱两可的，但1:00是模棱两可的。
        /// Mono将向后夏令时转换视为[1:00 DST->2:00 DST]-（1:00 ST-->2:00 ST]。因此2:00是模糊的，但1:00不是模糊的。
        /// 我们必须在ambiguousTime上加1分，才能使所有框架具有相同的行为。因此，1:00是模糊的，2:00不是模糊的。
        /// </summary>
        /// <param name="zone"> 时区信息。</param>
        /// <param name="ambiguousTime"> 模糊时间。</param>
        /// <returns></returns>
        public static bool IsAmbiguousTime(TimeZoneInfo zone, DateTime ambiguousTime)
        {
            return zone.IsAmbiguousTime(ambiguousTime.AddTicks(1));
        }

        /// <summary>
        /// 获取给定时间在给定时区中的夏令时偏移量。
        /// </summary>
        /// <param name="zone"> 时区信息。</param>
        /// <param name="ambiguousDateTime"> 模糊时间。</param>
        /// <returns></returns>
        public static TimeSpan GetDaylightOffset(TimeZoneInfo zone, DateTime ambiguousDateTime)
        {
            var offsets = GetAmbiguousOffsets(zone, ambiguousDateTime);
            var baseOffset = zone.BaseUtcOffset;

            if (offsets[0] != baseOffset) return offsets[0];

            return offsets[1];
        }

        /// <summary>
        /// 获取给定时间在给定时区中的夏令时结束时间。
        /// </summary>
        /// <param name="zone"> 时区信息。</param>
        /// <param name="invalidDateTime"> 无效时间。</param>
        /// <returns></returns>
        public static DateTimeOffset GetDaylightTimeStart(TimeZoneInfo zone, DateTime invalidDateTime)
        {
            var dstTransitionDateTime = new DateTime(invalidDateTime.Year, invalidDateTime.Month, invalidDateTime.Day,
                invalidDateTime.Hour, invalidDateTime.Minute, 0, 0, invalidDateTime.Kind);

            while (zone.IsInvalidTime(dstTransitionDateTime))
            {
                dstTransitionDateTime = dstTransitionDateTime.AddMinutes(1);
            }

            var dstOffset = zone.GetUtcOffset(dstTransitionDateTime);

            return new DateTimeOffset(dstTransitionDateTime, dstOffset);
        }

        /// <summary>
        /// 获取给定时间在给定时区中的夏令时开始时间。
        /// </summary>
        /// <param name="zone"> 时区信息。</param>
        /// <param name="ambiguousTime"> 模糊时间。</param>
        /// <param name="daylightOffset"> 夏令时偏移量。</param>
        /// <returns></returns>
        public static DateTimeOffset GetStandardTimeStart(TimeZoneInfo zone, DateTime ambiguousTime, TimeSpan daylightOffset)
        {
            var dstTransitionEnd = GetDstTransitionEndDateTime(zone, ambiguousTime);

            return new DateTimeOffset(dstTransitionEnd, daylightOffset).ToOffset(zone.BaseUtcOffset);
        }

        /// <summary>
        /// 获取给定时间在给定时区中的夏令时结束时间。
        /// </summary>
        /// <param name="zone"> 时区信息。</param>
        /// <param name="ambiguousTime"> 模糊时间。</param>
        /// <returns></returns>
        public static DateTimeOffset GetAmbiguousIntervalEnd(TimeZoneInfo zone, DateTime ambiguousTime)
        {
            var dstTransitionEnd = GetDstTransitionEndDateTime(zone, ambiguousTime);

            return new DateTimeOffset(dstTransitionEnd, zone.BaseUtcOffset);
        }

        /// <summary>
        /// 获取给定时间在给定时区中的夏令时结束时间。
        /// </summary>
        /// <param name="zone"> 时区信息。</param>
        /// <param name="ambiguousTime"> 模糊时间。</param>
        /// <param name="daylightOffset"> 夏令时偏移量。</param>
        /// <returns></returns>
        public static DateTimeOffset GetDaylightTimeEnd(TimeZoneInfo zone, DateTime ambiguousTime, TimeSpan daylightOffset)
        {
            var daylightTransitionEnd = GetDstTransitionEndDateTime(zone, ambiguousTime);

            return new DateTimeOffset(daylightTransitionEnd.AddTicks(-1), daylightOffset);
        }

        /// <summary>
        /// 获取给定时间在给定时区中的夏令时偏移量。
        /// </summary>
        /// <param name="zone"> 时区信息。</param>
        /// <param name="ambiguousTime"> 模糊时间。</param>
        /// <returns></returns>
        private static TimeSpan[] GetAmbiguousOffsets(TimeZoneInfo zone, DateTime ambiguousTime)
        {
            return zone.GetAmbiguousTimeOffsets(ambiguousTime.AddTicks(1));
        }

        /// <summary>
        /// 获取给定时间在给定时区中的夏令时结束时间。
        /// </summary>
        /// <param name="zone"> 时区信息。</param>
        /// <param name="ambiguousDateTime"> 模糊时间。</param>
        /// <returns></returns>
        private static DateTime GetDstTransitionEndDateTime(TimeZoneInfo zone, DateTime ambiguousDateTime)
        {
            var dstTransitionDateTime = new DateTime(ambiguousDateTime.Year, ambiguousDateTime.Month, ambiguousDateTime.Day,
                ambiguousDateTime.Hour, ambiguousDateTime.Minute, 0, 0, ambiguousDateTime.Kind);

            while (zone.IsAmbiguousTime(dstTransitionDateTime))
            {
                dstTransitionDateTime = dstTransitionDateTime.AddMinutes(1);
            }

            return dstTransitionDateTime;
        }
    }
}
